package org.javanum.number;

/**
 * <p>An immutable implementation of the {@link CartesianComplexField}
 * interface. This class uses primitive {@code double} elements to 
 * represent its real and imaginary parts. To reduce the number of 
 * autoboxings and auto-unboxings required, this class directly accesses its
 * memory whenever possible.</p>
 * 
 * Author: Scott Fines
 * Date: Oct 31, 2009
 *
 */
public class Complex extends
                     AbstractCartesianComplex<Real,Complex,Double> {

	/**
	 * The multiplicative identity as a singleton. This element is
     * returned by the method {@link #multiplicativeIdentity()}
	 */
	@SuppressWarnings({"WeakerAccess"})
    public static final Complex ONE = new Complex(1,0);
	/**
	 * The zero element as a singleton. This element is returned by the 
	 * method {@link #additiveIdentity()}.
	 */
	public static final Complex ZERO = new Complex(0,0){
        @Override
        public PolarComplex polarForm() {
            return PolarComplex.ZERO;
        }

        @Override
        public Double modulus() {
            return 0d;
        }

        @Override
        public Complex negate() {
            return Complex.ZERO;
        }

        @Override
        public Complex conjugate() {
            return Complex.ZERO;
        }
    };

    /**
     * <p>The imaginary element <i>i</i> as a singleton.</p>
     *
     * <p>I.multiply(I)==-1 by definition.</p>
     */
    public static final Complex I = new Complex(0,1);
	
	/**
	 * Complex infinity as a singleton.
	 */
	public static final Complex COMPLEX_INFINITY =
                  new Complex(Double.POSITIVE_INFINITY,
                              Double.POSITIVE_INFINITY){
                      @Override
                      public Complex conjugate() {
                          return COMPLEX_INFINITY;
                      }

                      @Override
                      public Double modulus() {
                          return Double.POSITIVE_INFINITY;
                      }

                      @Override
                      public Complex negate() {
                          return COMPLEX_INFINITY;
                      }

                      @Override
                      public PolarComplex polarForm() {
                          return PolarComplex.COMPLEX_INFINITY;
                      }
                  };

    /**
     * The Complex equivalent of Double.NaN
     */
    public static final Complex NaN =
            new Complex(Double.NaN,Double.NaN){
                @Override
                public PolarComplex polarForm() {
                    return PolarComplex.NaN;
                }

                @Override
                public Complex negate() {
                    return Complex.NaN;
                }

                @Override
                public Double modulus() {
                    return Double.NaN;
                }

                @Override
                public Complex conjugate() {
                    return super.conjugate();    //To change body of overridden methods use File | Settings | File Templates.
                }
            };

	/**
	 * the imaginary part of {@code this}
	 */
	public final double imaginary;
	
	/**
	 * the real part of {@code this}
	 */
	public final double real;

/*-----------------------------------------------*/
    /*Constructors*/

    private Complex(double real, double imaginary) {
		this.real = real;
		this.imaginary = imaginary;
	}

    private Complex(Real real, Real complex){
        this(real.value,complex.value);
	}

    /**
     * <p>Creates a new instance of this implementation from primitive
     * data types</p>
     *
     * @param real the real part
     * @param imaginary the imaginary part
     * @return a new instanceof the Complex class.
     */
    public static Complex valueOf(double real, double imaginary){
        return new Complex(real, imaginary);
    }

    /**
	 * <p>Creates a new instance of this implementation from Real objects.
	 * </p>
	 *
	 * <p>Note that passing in {@code null} is NOT allowed and will
	 * result in an IllegalArgumentException</p>
	 *
     * @param real the real part
     * @param imaginary the imaginary part
     * @return a new instance of Complex
	 * @throws NullPointerException if {@code null} is passed in.
	 */
    public static Complex valueOf(Real real, Real imaginary){
        if(real==null||imaginary==null)
            throw new IllegalArgumentException("Cannot construct a " +
                    "Complex out of null arguments");
        return new Complex(real,imaginary);
    }

/*-----------------------------------------------*/
    /*getter methods*/

    @Override
	public Real imaginaryPart() {
		return new Real(imaginary);
	}

    @Override
	public Real realPart() {
		return new Real(real);
	}

/*-----------------------------------------------*/
    /*add methods*/

    @Override
	public Complex add(Complex summand) {
        if(isNaN(this)||isNaN(summand))
            return NaN;
        if(isInfinite(this)||isInfinite(summand))
            return COMPLEX_INFINITY;

		return
            new Complex(summand.real+real,
                        summand.imaginary+imaginary);
	}

    @Override
    public Complex add(Real value) {
        if(isNaN(this)||Real.isNaN(value))
            return NaN;
        if(isInfinite(this)||Real.isInfinite(value))
            return COMPLEX_INFINITY;

        return new Complex(real+value.value,imaginary);
    }

    @Override
	public Complex additiveIdentity() {
		return ZERO;
	}

    @Override
	public Complex additiveInverse() {
		return new Complex(-real,-imaginary);
	}

/*-----------------------------------------------*/
    /*subtract methods*/

    @Override
	public Complex subtract(Complex value) {
        if(isNaN(this)||isNaN(value))
            return NaN;
        if(isInfinite(this)||isInfinite(value))
            return COMPLEX_INFINITY;

		return new Complex(real-value.real,
                            imaginary-value.imaginary);
	}
    
    @Override
    public Complex subtract(Real value) {
        if(isNaN(this)||Real.isNaN(value))
            return NaN;
        if(isInfinite(this)||Real.isInfinite(value))
            return COMPLEX_INFINITY;

        return new Complex(real-value.value,imaginary);
    }

/*-----------------------------------------------*/
    /*multiplication methods*/

    @Override
	public Complex multiply(Complex value) {
        if(isNaN(this)||isNaN(value))
            return NaN;

        if(isInfinite(this))return COMPLEX_INFINITY;

		double realValue =
                real*value.real-imaginary*value.imaginary;
		double imaginaryValue =
                real*value.imaginary+imaginary*value.real;
		return new Complex(realValue,imaginaryValue);
	}

    @Override
    public Complex multiply(Real value) {
        if(isNaN(this)||Real.isNaN(value))
            return NaN;

        if(isInfinite(this)||Real.isInfinite(value))
            return COMPLEX_INFINITY;

        return new Complex(real*value.value,imaginary*value.value);
    }

    @Override
	public Complex multiplicativeIdentity() {
		return Complex.ONE;
	}

    @Override
	public Complex multiplicativeInverse() {
		double scale = Math.pow(real, 2)+Math.pow(imaginary, 2);
		return new Complex(real/scale,-imaginary/scale);
	}

/*-----------------------------------------------*/
    /*division methods*/
        /**
     * <p>Divides {@code this} by the specified
     * Complex {@code divisor}.</p>
     *
     * <p>This implementation makes use of The division algorithm
     * originally stated by Robert Smith[6]</p>
     *
     * @param divisor the element to divide by
     * @return the result of dividing this by {@code divisor}
     * @throws ArithmeticException if
     *          {@code Complex.ZERO.equals(divisor)=true}
     */
	@Override
	public Complex divide(Complex divisor) {
        if(ZERO.equals(divisor))
            throw new ArithmeticException("Divide by zero");

        if(isNaN(this)||isNaN(divisor))
            return NaN;

        if(!isInfinite(this)&&isInfinite(divisor))return ZERO;

        double r,scale,newReal,newImaginary;
        if(Math.abs(divisor.real)>=Math.abs(divisor.imaginary)){
            r = divisor.imaginary/divisor.real;
            scale = divisor.real+r*divisor.imaginary;
            newReal = (real+imaginary*r)/scale;
            newImaginary = (imaginary-real*r)/scale;
        }else{
            r = divisor.real/divisor.imaginary;
            scale = divisor.imaginary+r*divisor.real;
            newReal = (real*r+imaginary)/scale;
            newImaginary = (imaginary*r-real)/scale;
        }
        return new Complex(newReal,newImaginary);
	}

    @Override
    public Complex divide(Real value) {
        if(Real.ZERO.equals(value))
            throw new ArithmeticException("Divide by zero");

        if(isNaN(this)||Real.isNaN(value))
            return NaN;
        if(!isInfinite(this)&&Real.isInfinite(value))
            return ZERO;
        else if(isInfinite(this)&&!Real.isInfinite(value))
            return COMPLEX_INFINITY;

        return new Complex(real/value.value,imaginary/value.value);
    }

/*-----------------------------------------------*/
    /*conjugate, modulus, and negations*/

    @Override
	public Complex conjugate() {
		return new Complex(real,-imaginary);
	}

    @Override
	public Double modulus() {
        if(isNaN(this))return Double.NaN;
        if(isInfinite(this))return Double.POSITIVE_INFINITY;
        if(this.equals(Complex.ZERO))return 0.0;

        if(Double.compare(Math.abs(real),Math.abs(imaginary))<0){
            return Math.sqrt(Math.pow(real/imaginary,2)+1)
                    *Math.abs(imaginary);
        }else{
            return Math.sqrt(Math.pow(imaginary/real,2)+1)
                    *Math.abs(real);
        }
	}

    /**
     * <p>Returns {@code this} negated.</p>
     *
     * <p>If {@code this = (x,y)} for doubles x and y,
     * then the result of this method is {@code (-x,-y)}</p>
     * @return the negation of {@code this}, as a new Complex
     */
    @Override
    public Complex negate(){
        return new Complex(-real,-imaginary);
    }

/*-----------------------------------------------*/
    /*Static utility and helper methods*/
    /**
     * <p>Determines whether or not the passed in Complex
     * is Infinite.</p>
     *
     * @param complex the complex to test
     * @return true if {@code complex} is Infinite according
     *          to the conditions specified by
     *          {@link Double.isInfinite(double)}.
     * @see {@link Double.isInfinite(double)}
     */
    public static boolean isInfinite(Complex complex) {
        return Double.isInfinite(complex.real) ||
                Double.isInfinite(complex.imaginary);
    }

    /**
     * <p>Determines whether or not the passed in Complex is
     * Not a Number(NaN), according to
     * {@link Double.compare(double,double)}</p>
     *
     * @param complex the number to be tested
     * @return true if {@code complex} is Not a Number
     *          according to the conditions specified by
     *          {@link Double.compare(double,double)}
     * @see {@link Double.compare(double, double)}
     */
    public static boolean isNaN(Complex complex) {
        return Double.compare(complex.real, Double.NaN) == 0 ||
                Double.compare(complex.imaginary, Double.NaN) == 0;
    }

    @Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (!(obj instanceof Complex))
			return false;
		Complex other = (Complex) obj;
        return Double.compare(real,other.real)==0&&
               Double.compare(imaginary,other.imaginary)==0;
	}

    @Override
	public int hashCode() {
		final int prime = 31;
		int result = 17;
		long temp;
		temp = Double.doubleToLongBits(imaginary);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(real);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@SuppressWarnings("unchecked") //Can suppress because PolarComplex
								   // is an implementation of the 
								   //specification
                                   //TODO figure out the
                                   //TODO generics here
	@Override
	public PolarComplex polarForm() {
        if(Double.compare(real,0)==0)
            return PolarComplex.valueOf(modulus(),Math.PI/2);
        if(Double.compare(imaginary,0)==0){
            double angle = real<0?-Math.PI:0d;
            return PolarComplex.valueOf(modulus(),angle);
        }

        double mod = modulus();
        double atan = Math.atan(imaginary/real);
        if(real<0&&imaginary>0)
            atan = atan+Math.PI;
        else if(real<0&&imaginary<0)
            atan = atan-Math.PI;

		return PolarComplex.valueOf(mod,atan);
	}

}
